// ipc_demo.c — Linux C 进程间通信（IPC）示例（标准C风格、所有if均加大括号、对齐）
// 编译：gcc -O2 -Wall -Wextra -o ipc_demo ipc_demo.c
// 运行：./ipc_demo [interval_sec] [M_size] [N_size]

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>

#define PAYLOAD_MAX (1<<20)

static const char* now_str(char* buf, size_t len)
{
    struct timespec ts;
    if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
    {
        snprintf(buf, len, "0000-00-00 00:00:00.000");
        return buf;
    }

    struct tm tmv;
    if (localtime_r(&ts.tv_sec, &tmv) == NULL)
    {
        snprintf(buf, len, "0000-00-00 00:00:00.000");
        return buf;
    }

    snprintf(buf, len, "%04d-%02d-%02d %02d:%02d:%02d.%03ld",
             tmv.tm_year + 1900, tmv.tm_mon + 1, tmv.tm_mday,
             tmv.tm_hour, tmv.tm_min, tmv.tm_sec, ts.tv_nsec / 1000000);
    return buf;
}

static ssize_t readn(int fd, void* buf, size_t n)
{
    size_t left = n;
    char* p = (char*)buf;

    while (left)
    {
        ssize_t r = read(fd, p, left);
        if (r == 0)
        {
            return (ssize_t)(n - left);
        }
        if (r < 0)
        {
            if (errno == EINTR)
            {
                continue;
            }
            return -1;
        }
        left -= (size_t)r;
        p += r;
    }

    return (ssize_t)n;
}

static ssize_t writen(int fd, const void* buf, size_t n)
{
    size_t left = n;
    const char* p = (const char*)buf;

    while (left)
    {
        ssize_t w = write(fd, p, left);
        if (w <= 0)
        {
            if (w < 0 && errno == EINTR)
            {
                continue;
            }
            return -1;
        }
        left -= (size_t)w;
        p += w;
    }

    return (ssize_t)n;
}

static uint32_t crc32_table[256];

static void crc32_init(void)
{
    for (uint32_t i = 0; i < 256; ++i)
    {
        uint32_t c = i;
        for (int j = 0; j < 8; ++j)
        {
            if (c & 1)
            {
                c = 0xEDB88320u ^ (c >> 1);
            }
            else
            {
                c = (c >> 1);
            }
        }
        crc32_table[i] = c;
    }
}

static uint32_t crc32_calc(const void* data, size_t len)
{
    const uint8_t* p = (const uint8_t*)data;
    uint32_t c = 0xFFFFFFFFu;

    for (size_t i = 0; i < len; ++i)
    {
        c = crc32_table[(c ^ p[i]) & 0xFFu] ^ (c >> 8);
    }

    return c ^ 0xFFFFFFFFu;
}

#pragma pack(push, 1)
typedef struct
{
    uint32_t seq;
    uint32_t size;
    uint32_t crc32;
} msg_hdr_t;

typedef struct
{
    uint32_t seq;
    uint32_t size;
    uint8_t  ok;
} ack_t;
#pragma pack(pop)

typedef struct
{
    uint64_t send_cnt_ok;
    uint64_t send_cnt_err;
    uint64_t recv_cnt_ok;
    uint64_t recv_cnt_err;
    uint64_t bytes_sent_ok;
    uint64_t bytes_sent_err;
    uint64_t bytes_recv_ok;
    uint64_t bytes_recv_err;
} stats_t;

static volatile sig_atomic_t g_stop = 0;

static void on_sigint(int signo)
{
    (void)signo;
    g_stop = 1;
}

static void print_stats(const char* who, const stats_t* s)
{
    fprintf(stderr,
    "========== 最终统计（%s）==========\n"
    "发送成功: %llu 次, %llu 字节\n"
    "发送失败: %llu 次, %llu 字节\n"
    "接收成功: %llu 次, %llu 字节\n"
    "接收失败: %llu 次, %llu 字节\n"
    "发送总计: %llu 字节\n"
    "接收总计: %llu 字节\n",
    who,
    (unsigned long long)s->send_cnt_ok, (unsigned long long)s->bytes_sent_ok,
    (unsigned long long)s->send_cnt_err, (unsigned long long)s->bytes_sent_err,
    (unsigned long long)s->recv_cnt_ok, (unsigned long long)s->bytes_recv_ok,
    (unsigned long long)s->recv_cnt_err, (unsigned long long)s->bytes_recv_err,
    (unsigned long long)(s->bytes_sent_ok + s->bytes_sent_err),
    (unsigned long long)(s->bytes_recv_ok + s->bytes_recv_err));
}

static void process_A(int fd_send, int fd_ack, unsigned interval_s, unsigned M, unsigned N)
{
    stats_t st;
    memset(&st, 0, sizeof(st));

    uint32_t seq = 0;
    char tbuf[64];
    uint8_t* payload = (uint8_t*)malloc(PAYLOAD_MAX);

    if (!payload)
    {
        perror("malloc");
        exit(1);
    }

    srand((unsigned)time(NULL) ^ (unsigned)getpid());

    while (!g_stop)
    {
        ++seq;
        unsigned sz = (seq % 2 == 1) ? M : N;
        if (sz > PAYLOAD_MAX)
        {
            sz = PAYLOAD_MAX;
        }

        for (unsigned i = 0; i < sz; ++i)
        {
            payload[i] = (uint8_t)(rand() & 0xFF);
        }

        msg_hdr_t hdr;
        hdr.seq = seq;
        hdr.size = sz;
        hdr.crc32 = crc32_calc(payload, sz);

        int make_bad = (seq % 2 == 0);
        uint32_t sent_crc = hdr.crc32;
        if (make_bad)
        {
            sent_crc ^= 0xDEADBEEF;
        }

        msg_hdr_t hdr_to_send = hdr;
        hdr_to_send.crc32 = sent_crc;

        if (writen(fd_send, &hdr_to_send, sizeof(hdr_to_send)) != (ssize_t)sizeof(hdr_to_send))
        {
            perror("A writen header");
            break;
        }

        if (writen(fd_send, payload, sz) != (ssize_t)sz)
        {
            perror("A writen payload");
            break;
        }

        now_str(tbuf, sizeof(tbuf));
        printf("%s 第%u次:  A:我发送了 %u 字节数据给 B (期望CRC=%08X, 实际发送CRC=%08X)\n",
               tbuf, seq, sz, hdr.crc32, hdr_to_send.crc32);
        fflush(stdout);

        ack_t ack;
        ssize_t r = readn(fd_ack, &ack, sizeof(ack));
        if (r != (ssize_t)sizeof(ack))
        {
            perror("A read ack");
            break;
        }

        now_str(tbuf, sizeof(tbuf));
        if (ack.ok)
        {
            printf("%s 第%u次:  A:B告诉我收到了 %u 字节数据，校验正确",
                   tbuf, ack.seq, ack.size);
            st.send_cnt_ok++;
            st.bytes_sent_ok += sz;
        }
        else
        {
            printf("%s 第%u次:  A:B告诉我收到了 %u 字节数据，但校验失败",
                   tbuf, ack.seq, ack.size);
            st.send_cnt_err++;
            st.bytes_sent_err += sz;
        }
        fflush(stdout);

        for (unsigned i = 0; i < interval_s && !g_stop; ++i)
        {
            sleep(1);
        }
    }

    print_stats("进程A", &st);
    free(payload);
}

static void process_B(int fd_recv, int fd_ack)
{
    stats_t st;
    memset(&st, 0, sizeof(st));

    char tbuf[64];
    uint8_t* payload = (uint8_t*)malloc(PAYLOAD_MAX);

    if (!payload)
    {
        perror("malloc");
        exit(1);
    }

    while (!g_stop)
    {
        msg_hdr_t hdr;
        ssize_t r = readn(fd_recv, &hdr, sizeof(hdr));

        if (r == 0)
        {
            break;
        }

        if (r != (ssize_t)sizeof(hdr))
        {
            perror("B read hdr");
            break;
        }

        if (hdr.size > PAYLOAD_MAX)
        {
            fprintf(stderr, "B: size too big %u", hdr.size);
            break;
        }

        if (readn(fd_recv, payload, hdr.size) != (ssize_t)hdr.size)
        {
            perror("B read payload");
            break;
        }

        uint32_t crc_calc = crc32_calc(payload, hdr.size);
        int ok = 0;
        if (crc_calc == hdr.crc32)
        {
            ok = 1;
        }
        else
        {
            ok = 0;
        }

        now_str(tbuf, sizeof(tbuf));
        if (ok)
        {
            printf("%s 第%u次:  B:我从A接收了 %u 字节数据，校验正确，通知A接收了%u字节数据，校验正确。",
                   tbuf, hdr.seq, hdr.size, hdr.size);
            st.recv_cnt_ok++;
            st.bytes_recv_ok += hdr.size;
        }
        else
        {
            printf("%s 第%u次:  B:我从A接收了 %u 字节数据，校验错误，通知A接收了%u字节数据，校验错误",
                   tbuf, hdr.seq, hdr.size, hdr.size);
            st.recv_cnt_err++;
            st.bytes_recv_err += hdr.size;
        }
        fflush(stdout);

        ack_t ack;
        ack.seq = hdr.seq;
        ack.size = hdr.size;
        ack.ok = (uint8_t)ok;

        if (writen(fd_ack, &ack, sizeof(ack)) != (ssize_t)sizeof(ack))
        {
            perror("B write ack");
            break;
        }
    }

    print_stats("进程B", &st);
    free(payload);
}

int main(int argc, char** argv)
{
    unsigned interval_s = 1;
    unsigned M = 1024;
    unsigned N = 2048;

    if (argc >= 2)
    {
        interval_s = (unsigned)strtoul(argv[1], NULL, 10);
    }

    if (argc >= 3)
    {
        M = (unsigned)strtoul(argv[2], NULL, 10);
    }

    if (argc >= 4)
    {
        N = (unsigned)strtoul(argv[3], NULL, 10);
    }

    if (M > PAYLOAD_MAX || N > PAYLOAD_MAX)
    {
        fprintf(stderr, "M/N 过大，最大允许 %d", PAYLOAD_MAX);
        return 2;
    }

    if (signal(SIGINT, on_sigint) == SIG_ERR)
    {
        perror("signal");
        return 1;
    }

    crc32_init();
    int p_data[2];
    int p_ack[2];

    if (pipe(p_data) < 0)
    {
        perror("pipe data");
        return 1;
    }

    if (pipe(p_ack) < 0)
    {
        perror("pipe ack");
        return 1;
    }

    pid_t pid = fork();

    if (pid < 0)
    {
        perror("fork");
        return 1;
    }

    if (pid == 0)
    {
        close(p_data[1]);
        close(p_ack[0]);
        process_B(p_data[0], p_ack[1]);
        close(p_data[0]);
        close(p_ack[1]);
        return 0;
    }
    else
    {
        close(p_data[0]);
        close(p_ack[1]);
        process_A(p_data[1], p_ack[0], interval_s, M, N);
        close(p_data[1]);
        close(p_ack[0]);
        return 0;
    }
}

